package com.ibeeking.gateway.cp.config.uaa;

import cn.hutool.core.util.ArrayUtil;
import com.ibeeking.gateway.cp.constant.LogUtils;
import com.ibeeking.gateway.cp.security.AuthorizationManager;
import com.ibeeking.found.common.constants.HeaderNounConstant;
import com.ibeeking.found.common.response.ResponseCodeEnum;
import com.ibeeking.nematos.utils.json.JsonUtils;
import com.ibeeking.nematos.utils.result.ResponseResult;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

/**
 * 资源服务器配置
 *
 * @Author ibeeking
 */
@AllArgsConstructor
@Configuration
@EnableWebFluxSecurity
public class ResourceServerConfig {

    private final AuthorizationManager authorizationManager;

    private final WhiteListConfig whiteListConfig;

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {

        for (String url : whiteListConfig.getUrls()){
            LogUtils.info("url={}",url);
        }
        //token转换器
//        ServerBearerTokenAuthenticationConverter tokenAuthenticationConverter = new ServerBearerTokenAuthenticationConverter();
//        tokenAuthenticationConverter.setAllowUriQueryParameter(true);
//        //oauth2认证过滤器
//        AuthenticationWebFilter oauth2Filter = new AuthenticationWebFilter(customAuthenticationManager);
//        oauth2Filter.setServerAuthenticationConverter(tokenAuthenticationConverter);
//        oauth2Filter.setAuthenticationFailureHandler(new ServerAuthenticationEntryPointFailureHandler( new JsonAuthenticationEntryPoint()));
//        oauth2Filter.setAuthenticationSuccessHandler(new Oauth2AuthSuccessHandler());
//        //oauth2Filter.setRequiresAuthenticationMatcher(new CustomServerWebExchangeMatchers(securityProperties));
//        http.addFilterAt(oauth2Filter, SecurityWebFiltersOrder.AUTHENTICATION);

        http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverter());
        http.oauth2ResourceServer().authenticationEntryPoint(authenticationEntryPoint());
        http.authorizeExchange()
                .pathMatchers(ArrayUtil.toArray(whiteListConfig.getUrls(), String.class)).permitAll()
                .pathMatchers(HttpMethod.OPTIONS).permitAll()
                .anyExchange().access(authorizationManager)
                .and()
                .exceptionHandling()
                //处理未授权
                .accessDeniedHandler(accessDeniedHandler())
                //处理未认证
                .authenticationEntryPoint(authenticationEntryPoint())
                .and().csrf().disable();
        return http.build();
    }


    /**
     * 未授权
     *
     * @return
     */
    @Bean
    ServerAccessDeniedHandler accessDeniedHandler() {
        return (exchange, denied) -> {
            Mono<Void> mono = Mono.defer(() -> Mono.just(exchange.getResponse()))
                    .flatMap(response -> {
                        DataBuffer buffer = responseSetting(response, HttpStatus.FORBIDDEN, HttpStatus.FORBIDDEN.value(), ResponseCodeEnum.TOKEN_AUTHORIZATION_ERROR.getMsg());
                        return response.writeWith(Mono.just(buffer)).doOnError(error -> DataBufferUtils.release(buffer));
                    });

            return mono;
        };
    }

    /**
     * token无效或者已过期自定义响应
     */
    @Bean
    ServerAuthenticationEntryPoint authenticationEntryPoint() {
        return (exchange, e) -> {
            Mono<Void> mono = Mono.defer(() -> Mono.just(exchange.getResponse()))
                    .flatMap(response -> {
                        DataBuffer buffer = responseSetting(response, HttpStatus.UNAUTHORIZED, HttpStatus.UNAUTHORIZED.value(), ResponseCodeEnum.TOKEN_EXPIRE_ERROR.getMsg());
                        return response.writeWith(Mono.just(buffer)).doOnError(error -> DataBufferUtils.release(buffer));
                    });
            return mono;
        };
    }

    /***
     * 封装响应
     * @param response
     */
    private DataBuffer responseSetting(ServerHttpResponse response, HttpStatus statusCode, Integer code, String msg) {
        response.setStatusCode(statusCode);
        response.getHeaders().set(HeaderNounConstant.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        response.getHeaders().set(HeaderNounConstant.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
        response.getHeaders().set(HeaderNounConstant.CACHE_CONTROL, "no-cache");
        return response.bufferFactory().wrap(JsonUtils.toJsonStr(ResponseResult.fail(code, msg)).getBytes(StandardCharsets.UTF_8));
    }

    @Bean
    public Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        jwtGrantedAuthoritiesConverter.setAuthorityPrefix(HeaderNounConstant.JWT_AUTHORITY_PREFIX);
        jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(HeaderNounConstant.AUTHORITY_CLAIM_NAME);

        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
        return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
    }
}
